var GameApp;
(function (GameApp) {
    var Hello = (function () {
        function Hello() { }
        Hello.prototype.sayHello = function () {
            alert("Hello there.");
        };
        return Hello;
    })();
    GameApp.Hello = Hello;    
})(GameApp || (GameApp = {}));

var app = new GameApp.Hello();
function sayHi() {
    app.sayHello();
}
var TroopTypes;
(function (TroopTypes) {
    TroopTypes._map = [];
    TroopTypes._map[0] = "SamuraiSwordman";
    TroopTypes.SamuraiSwordman = 0;
    TroopTypes._map[1] = "Ronin";
    TroopTypes.Ronin = 1;
    TroopTypes._map[2] = "AshiguruSpearman";
    TroopTypes.AshiguruSpearman = 2;
    TroopTypes._map[3] = "Diaymo";
    TroopTypes.Diaymo = 3;
    TroopTypes._map[4] = "AshiguruMusketman";
    TroopTypes.AshiguruMusketman = 4;
    TroopTypes._map[5] = "SamuraiBowman";
    TroopTypes.SamuraiBowman = 5;
})(TroopTypes || (TroopTypes = {}));

var Troop = (function () {
    function Troop(troopType) {
        this._troopType = troopType;
        this._isSamurai = false;
        this._isAshiguru = false;
        this._isRange = false;
        switch(troopType) {
            case TroopTypes.SamuraiSwordman: {
                this._isRange = false;
                this._attackValue = 5;
                this._isSamurai = true;
                break;

            }
            case TroopTypes.Ronin: {
                this._isRange = false;
                this._attackValue = 5;
                this._isRonin = true;
                break;

            }
            case TroopTypes.AshiguruSpearman: {
                this._isRange = false;
                this._attackValue = 4;
                this._isAshiguru = true;
                break;

            }
            case TroopTypes.Diaymo: {
                this._isRange = false;
                this._attackValue = 6;
                break;

            }
            case TroopTypes.AshiguruMusketman: {
                this._isRange = true;
                this._attackValue = 4;
                this._isAshiguru = true;
                break;

            }
            case TroopTypes.SamuraiBowman: {
                this._isRange = true;
                this._attackValue = 6;
                this._isSamurai = true;
                break;

            }
        }
    }
    Object.defineProperty(Troop.prototype, "IsRange", {
        get: function () {
            return this._isRange;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Troop.prototype, "TroopType", {
        get: function () {
            return this._troopType;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Troop.prototype, "AttackValue", {
        get: function () {
            return this._attackValue;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Troop.prototype, "IsSamurai", {
        get: function () {
            return this._isSamurai;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Troop.prototype, "IsAshiguru", {
        get: function () {
            return this._isAshiguru;
        },
        enumerable: true,
        configurable: true
    });
    return Troop;
})();
var Utilities;
(function (Utilities) {
    function removeFirst(array, match) {
        for(var index = 0; index < array.length; ++index) {
            var candidate = array[index];
            if(match(candidate)) {
                array.splice(index, 1);
                break;
            }
        }
    }
    Utilities.removeFirst = removeFirst;
    function count(array, match) {
        var count = 0;
        for(var index = 0; index < array.length; ++index) {
            var candidate = array[index];
            if(match(candidate)) {
                ++count;
            }
        }
        return count;
    }
    Utilities.count = count;
})(Utilities || (Utilities = {}));

var __extends = this.__extends || function (d, b) {
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
}
var Exception = (function () {
    function Exception(message, innerException) {
        this._message = message;
        this._innerException = innerException;
    }
    Object.defineProperty(Exception.prototype, "Message", {
        get: function () {
            return this._message;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Exception.prototype, "InnerException", {
        get: function () {
            return this._innerException;
        },
        enumerable: true,
        configurable: true
    });
    return Exception;
})();
var TroopConstraintViolationException = (function (_super) {
    __extends(TroopConstraintViolationException, _super);
    function TroopConstraintViolationException(message, innerException) {
        _super.call(this, message, innerException);
    }
    return TroopConstraintViolationException;
})(Exception);
var ServiceLocator = (function () {
    function ServiceLocator() { }
    ServiceLocator.prototype.RegisterDiaymoArmy = function (createFunc) {
        this._diaymoArmyCreate = createFunc;
    };
    ServiceLocator.prototype.GetDiaymoArmy = function (name) {
        return this._diaymoArmyCreate(name);
    };
    ServiceLocator.prototype.RegisterTerritoryArmy = function (createFunc) {
        this._territoryArmyCreate = createFunc;
    };
    ServiceLocator.prototype.GetTerritoryArmy = function () {
        return this._territoryArmyCreate();
    };
    return ServiceLocator;
})();
var Army = (function () {
    function Army(maximumShogunTroops) {
        this._maximumShogunTroops = maximumShogunTroops;
        this._shogunTroops = [];
        this._roninTroops = [];
    }
    Army.prototype.AddTroop = function (troop) {
        if(troop.TroopType == TroopTypes.Ronin) {
            if(!this.getRoninSlotsAvailable()) {
                throw new RangeException();
            }
            this._roninTroops.push(troop);
        } else {
            if(!this.getShogunSlotsAvailable()) {
                throw new RangeException();
            }
            this._shogunTroops.push(troop);
        }
    };
    Army.prototype.RemoveTroop = function (troop) {
        this.RemoveTroopByType(troop.TroopType);
    };
    Army.prototype.RemoveTroopByType = function (troopType) {
        if(!this.getCanRemoveTroopByType(troopType)) {
            throw new TroopConstraintViolationException("You must always have at least one more shogun troop in your army than Ronin troops.");
        }
        if(troopType == TroopTypes.Ronin) {
            this._roninTroops.pop();
        } else {
            Utilities.removeFirst(this._shogunTroops, function (candidate) {
                return troopType == candidate.TroopType;
            });
        }
    };
    Army.prototype.getCanRemoveTroopByType = function (troopType) {
        var troopCount = this.getTroopCountByType(troopType);
        if(troopCount == 0) {
            return false;
        } else {
            if(troopType != TroopTypes.Ronin) {
                return this.getCanRemoveShogunTroop();
            }
        }
        return true;
    };
    Army.prototype.getTroopCountByType = function (troopType) {
        if(troopType == TroopTypes.Ronin) {
            return this._roninTroops.length;
        } else {
            return Utilities.count(this._shogunTroops, function (candidate) {
                return candidate.TroopType == troopType;
            });
        }
    };
    Army.prototype.ClearRoninTroops = function () {
        this._roninTroops = [];
    };
    Army.prototype.getRoninSlotsAvailable = function () {
        return this._roninTroops.length < this._shogunTroops.length - 1;
    };
    Army.prototype.getCanRemoveShogunTroop = function () {
        if(this._roninTroops.length == 0) {
            return this._shogunTroops.length > 0;
        } else {
            var shogunTroopLead = this._shogunTroops.length - this._roninTroops.length;
            return shogunTroopLead > 1;
        }
    };
    Army.prototype.getShogunSlotsAvailable = function () {
        return this._shogunTroops.length < this._maximumShogunTroops;
    };
    Army.prototype.getAllTroops = function () {
        var allTroops = [];
        for(var index = 0; index < this._shogunTroops.length; ++index) {
            allTroops.push(this._shogunTroops[index]);
        }
        for(var index = 0; index < this._roninTroops.length; ++index) {
            allTroops.push(this._roninTroops[index]);
        }
        return allTroops;
    };
    Object.defineProperty(Army.prototype, "MaximumRoninTroops", {
        get: function () {
            return this._maximumShogunTroops - 1;
        },
        enumerable: true,
        configurable: true
    });
    return Army;
})();
var TerritoryArmy = (function (_super) {
    __extends(TerritoryArmy, _super);
    function TerritoryArmy() {
        _super.call(this, TerritoryArmy.MaxTerritoryArmySize);
    }
    TerritoryArmy.MaxTerritoryArmySize = 5;
    return TerritoryArmy;
})(Army);
var DiamyoArmy = (function (_super) {
    __extends(DiamyoArmy, _super);
    function DiamyoArmy(name) {
        _super.call(this, DiamyoArmy.MaxDiaymoArmySize);
        this._name = name;
        this._samuraiWarriorCount = 0;
        this._ashiguruWarriorCount = 0;
    }
    DiamyoArmy.MaxDiaymoArmySize = 16;
    DiamyoArmy.MaxSamuariSlots = 8;
    DiamyoArmy.MaxAshiguruSlots = 8;
    DiamyoArmy.prototype.AddTroop = function (troop) {
        if(troop.TroopType == TroopTypes.Ronin) {
            _super.prototype.AddTroop.call(this, troop);
        } else {
            if(troop.IsSamurai) {
                if(!this.getSamuraiSlotAvailable()) {
                    throw new RangeException();
                }
                ++this._samuraiWarriorCount;
                _super.prototype.AddTroop.call(this, troop);
            } else {
                if(!this.getAshiguruSlotAvailable()) {
                    throw new RangeException();
                }
                ++this._ashiguruWarriorCount;
                _super.prototype.AddTroop.call(this, troop);
            }
        }
    };
    DiamyoArmy.prototype.RemoveTroop = function (troop) {
        _super.prototype.RemoveTroop.call(this, troop);
        if(troop.IsSamurai) {
            --this._samuraiWarriorCount;
        } else {
            --this._ashiguruWarriorCount;
        }
    };
    DiamyoArmy.prototype.getSamuraiSlotAvailable = function () {
        return this._samuraiWarriorCount < DiamyoArmy.MaxSamuariSlots && this.getShogunSlotsAvailable();
    };
    DiamyoArmy.prototype.getAshiguruSlotAvailable = function () {
        return this._ashiguruWarriorCount < DiamyoArmy.MaxAshiguruSlots && this.getShogunSlotsAvailable();
    };
    DiamyoArmy.prototype.getName = function () {
        return this._name;
    };
    return DiamyoArmy;
})(Army);
